package edu.byu.intex2.data.dao;

import edu.byu.intex2.data.*;
import edu.byu.intex2.data.bo.*;
import java.sql.*;

/**
 * TaskCC Data Access Object
 * @author Jason Whiting
 */
public class TaskCCDAO
{

    /////////////////////////////////////////////
    ///   Singleton code
    private static TaskCCDAO instance = null;

    /** Creates a new instance of TaskCCDAO */
    private TaskCCDAO()
    {
    }//constructor

    /** Retrieves the single instance of this class */
    public static synchronized TaskCCDAO getInstance()
    {
        if (instance == null)
        {
            instance = new TaskCCDAO();
        }
        return instance;
    }//getInstance

    ////////////////////////////////////////////
    ///   CREATE methods
    /**
     * Creates a new task_cc in the database
     * @param id GUID
     * @param conceptualCarId id of conceptualCar
     */
    public TaskCC create(String taskId, String conceptualCarId) throws DataException
    {
        TaskCC task_cc = new TaskCC(taskId + conceptualCarId); // generate a guid for the tasksLine even though we never use this...
        task_cc.setObjectAlreadyInDB(false);

        // Set Task ID
        task_cc.setTaskId(taskId);

        // Set ConceptualCar
        task_cc.setConceptualCarId(conceptualCarId);

        Cache.getInstance().put(task_cc.getId(), task_cc); // put in cache just in case...
        return task_cc;
    }//create


    ////////////////////////////////////////////
    ///   READ methods
    /**
     * Reads an existing task_cc from the database     */
    public TaskCC read(String taskId, String conceptualCarId) throws DataException
    {
        // Check Cache -- combined key
        if (Cache.getInstance().containsKey(taskId + conceptualCarId))
        {
            return (TaskCC) Cache.getInstance().get(taskId + conceptualCarId);
        }

        // Get Connection
        Connection conn = ConnectionPool.getInstance().get();

        // Try Reading with connection
        try
        {
            return read(taskId, conceptualCarId, conn);
        } catch (Exception e)
        {
            throw new DataException("An error occurred while reading the TaskCC object information.", e);
        } finally
        {
            ConnectionPool.getInstance().release(conn);
        }
    }

    /**
     * Internal method to read an existing task_cc from the database
     * @return task_cc
     */
    synchronized TaskCC read(String taskId, String conceptualCarId, Connection conn) throws Exception
    {
        // Check Cache -- combined key
        if (Cache.getInstance().containsKey(taskId + conceptualCarId))
        {
            return (TaskCC) Cache.getInstance().get(taskId + conceptualCarId);
        }

        // Prepare a SQL Statement
        PreparedStatement stmt = conn.prepareStatement("SELECT * FROM task_cc where task_id=? AND conceptual_car_id=?");

        try
        {
            stmt.setString(1, taskId);
            stmt.setString(2, conceptualCarId);
            ResultSet rs = stmt.executeQuery(); // execute query
            if (rs.next())
            {
                // Read the record with the result set
                return readRecord(rs, conn);
            }//if
            throw new DataException("TaskCC with taskId '" + taskId + "' and conceptualCarId '" + conceptualCarId + "' not found.");
        } finally
        {
            stmt.close();
        }
    }//read

    /** Internal method to create a task_cc object from a record  */
    synchronized TaskCC readRecord(ResultSet rs, Connection conn) throws Exception
    {
        String combinedGuid = rs.getString("task_id") + rs.getString("conceptual_car_id");
        if (Cache.getInstance().containsKey(combinedGuid))
        {
            return (TaskCC) Cache.getInstance().get(combinedGuid);
        }

        // Create new task_cc object
        TaskCC task_cc = new TaskCC(combinedGuid); // generate a new local GUID.  It won't be saved to the DB

        // TaskCC is already in DB
        task_cc.setObjectAlreadyInDB(true);

        // Put task_cc in cache
        Cache.getInstance().put(task_cc.getId(), task_cc);

        // Set TaskCC Attributes
        task_cc.setLaborTime(rs.getInt("labor_time"));
        task_cc.setTaskId(rs.getString("task_id"));
        task_cc.setConceptualCarId(rs.getString("conceptual_car_id"));

        // TaskCC is dirty and needs to be saved to DB
        task_cc.setDirty(false);

        return task_cc;
    }//readRecord


    /////////////////////////////////////////////
    ///   UPDATE methods
    /** Saves an existing task_cc in the database */
    public void save(TaskCC task_cc) throws DataException
    {
        // get a connection from the pool, save all of the tasks first using the other task_cc method and then commit them in the database
        Connection conn = ConnectionPool.getInstance().get();

        try
        {
            save(task_cc, conn); // save with connection
            conn.commit();  // commit the db transaction
        } catch (Exception e)
        {
            try
            {
                //rollback if there were any errors saving, catch any other errors you might have
                conn.rollback();
            } catch (SQLException e2)
            {
                throw new DataException("Could not roll back the task_cc database transaction!", e2);
            }
            throw new DataException("An error occurred while saving the task_cc object information.", e);
        } finally
        {
            // release the connection
            ConnectionPool.getInstance().release(conn);
        }
    }

    /** Internal method to update a task_cc in the database */
    void save(TaskCC task_cc, Connection conn) throws Exception
    {
        Cache.getInstance().put(task_cc.getId(), task_cc);

        if (task_cc.isObjectAlreadyInDB() && task_cc.isDirty())
        {
            update(task_cc, conn);
        } else if (task_cc.isDirty())
        {
            insert(task_cc, conn);
        }//if
    }

    /** External method to update or create a task_cc in the database without committing connection */
    public void saveNoCommit(TaskCC task_cc, Connection conn) throws Exception
    {
        // Get Already in Databsae
        Boolean alreadyInDB = task_cc.isObjectAlreadyInDB();

        if (alreadyInDB)
        {
            update(task_cc, conn);
        } else
        {
            insert(task_cc, conn);
        }
    }

    /** Saves an existing task_cc to the database */
    private void update(TaskCC task_cc, Connection conn) throws Exception
    {
        // use a prepared statement to prevent sql injection
        PreparedStatement stmt = conn.prepareStatement("UPDATE task_cc SET labor_time=? WHERE conceptual_car_id=? AND task_id=?");
        try
        {
            // Get TaskCC Attributes and add them to the statement
            stmt.setDouble(1, task_cc.getLaborTime());
            stmt.setString(2, task_cc.getConceptualCarId());
            stmt.setString(3, task_cc.getTaskId());
            stmt.execute();

            // TaskCC is clean!
            task_cc.setDirty(false);
        } finally
        {
            stmt.close();  // make sure to close the prepared statement
        }
    }

    /** Inserts a new task_cc into the database */
    private void insert(TaskCC task_cc, Connection conn) throws Exception
    {
        PreparedStatement stmt = conn.prepareStatement("INSERT INTO task_cc (labor_time, conceptual_car_id, task_id) VALUES (?, ?, ?)");
        try
        {
            stmt.setDouble(1, task_cc.getLaborTime());
            stmt.setString(2, task_cc.getConceptualCarId());
            stmt.setString(3, task_cc.getTaskId());
            stmt.execute();

            task_cc.setObjectAlreadyInDB(true); // now in DB
            task_cc.setDirty(false); // object is clean
        } catch (Exception e)
        {
            e.printStackTrace();
        } finally
        {
            stmt.close(); // close the prepared statement
        }
    }

    /////////////////////////////////////////////////
    ///   DELETE methods
    /** We do not support deleting of business objects in this application */
    public void delete(TaskCC task_cc) throws DataException
    {
        throw new RuntimeException("Nice try. The delete function is not supported in this application.");
    }
}
